<?php

namespace EasyVote\EasyVoteBundle\Controller;

use DateTime;
use DateInterval;
use EasyVote\EasyVoteBundle\Entity\ElectionReminder;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use EasyVote\EasyVoteBundle\Form\EntityStorage;
use EasyVote\EasyVoteBundle\Entity\Election;
use EasyVote\EasyVoteBundle\Entity\Profile;
use EasyVote\EasyVoteBundle\Entity\Vote;
use EasyVote\EasyVoteBundle\Repository\UserRepository;
use Ob\HighchartsBundle\Highcharts\Highchart;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraints\Date;

/**
 * Class ElectionsController
 * Bridges the gap between all election-related views and database entities.
 * @package EasyVote\EasyVoteBundle\Controller
 */
class ElectionsController extends Controller
{
    /**
     * @Template
     * This action is used to show the list of elections
     * Inserts the election list and whether or not the current user is an admin
     */
    public function showAction()
    {
        $retArr = array();
		
		// Get the current user
       $retArr['user'] = $this->getUser();
        
        // Return the list of all elections
		$em = $this->getDoctrine()->getManager();
		$repo = $em->getRepository('EasyVoteBundle:Election');
        $retArr['elections'] = $repo->findAll();
        
        // Return true if the user is an admin
        $retArr['isAdmin'] = $this->getUser()->isAdmin();
		
		// Return true if the user needs to update their profile
		$retArr['isProfileComplete'] = UserRepository::isProfileComplete($this->getUser()->getProfile());

        return $retArr;
    }
    
    /**
     * @Template
     * Form to create a new election
     * @param Request $request the request
     * @return Response A form to add a new election, or a redirect to the election listing.
     */
    public function newAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();
		$election = new Election();

        //lock down
        $user = $this->getUser();
        if(!$user->isAdmin()){
            return $this->redirect($this->generateUrl('elections_show'));
        }
		
		// The form to fill out the new election object
		$form = $this->createFormBuilder($election)
			->add('title', 'text', array(
					'required' => true
				)
			)
			->add('summary', 'text', array(
					'required' => true
				)
			)
			->add('text', 'textarea', array(
					'required' => true
				)
			)
			->add('optionSubmissionStartDate', 'date', array(
					'data' => new DateTime()
				)
			)
			->add('optionSubmissionEndDate', 'date', array(
					'data' => ((new DateTime())->add(new DateInterval('P1D')))
				)
			)
			->add('votingStartDate', 'date', array(
					'data' => new DateTime()
				)
			)
			->add('votingEndDate', 'date', array(
					'data' => ((new DateTime())->add(new DateInterval('P1D')))
				)
			)
			->add('classifications', 'entity', array(
					'class' => 'EasyVoteBundle:Classification', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'multiple' => true
				)
			)
			->add('degrees', 'entity', array(
					'class' => 'EasyVoteBundle:Degree', 
					'property' => 'displayName' , 
					'empty_value' => 'Choose an option',
					'required' => false, 
					'multiple' => true
				)
			)
			/*
		    ->add('departments', 'entity', array(
					'class' => 'EasyVoteBundle:Department', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'required' => false,
					'multiple' => true
				)
			)
		    ->add('jobs', 'entity', array(
					'class' => 'EasyVoteBundle:Job', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'required' => false,
					'multiple' => true
				)
			)	    
			*/
		    ->add('imageURL', 'url', array(
					'data' => "http://placehold.it/200x200"
				)
		    )
		    ->add('altText', 'text')
		    ->add('writeInsAllowed', 'checkbox', array('data' => true, 'label' => 'Check if write in candidates are allowed.', 'required' => false))
		    ->add('isCandidateElection', 'checkbox', array('data' => true, 'label' => 'Check if this is a candidate election.', 'required' => false))
		    ->add('save', 'submit', array('label' => 'Create Election'))
            ->getForm();

        //this block has "what to do if form is being submitted"
        if ($request->getMethod() == 'POST') 
        {
            $form->handleRequest($request);

            if ($form->isValid()) 
            {
                // Saves the election to the database
                $em->persist($election);
                $em->flush();

                //redirect
                return $this->redirect($this->generateUrl('elections_show'));
            }
        }

        //this block has "show the form"
        return $this->render('EasyVoteBundle:Elections:new.html.twig', array('form_view' => $form->createView()));
    }

	/**
	 * Form to remove an election.
     * @param int $id the ID of the election
     * @return Response the list of elections.
	 */
	public function removeAction($id)
    {
		// Find the election with an id of $id in the Database
        $em = $this->getDoctrine()->getManager();
        $electionRepo = $em->getRepository('EasyVoteBundle:Election');
        $election = $electionRepo->findOneById($id);

        //case 0: insufficient rights
        if (!$this->getUser()->isAdmin()) {
            return $this->redirect($this->generateUrl('elections_show'));
        }

        //case 1: null election
        if (empty($election)) {
            return $this->redirect($this->generateUrl('elections_show'));
        }

        //otherwise, delete election
        //but first purge votes
        $votes = $election->getVotes();
        foreach ($votes as $vote) {
            $election->removeVote($vote);
            $em->persist($election);
            $em->flush();
            $em->remove($vote);
            $em->flush();
        }

        //purge reminders
        $reminders = $election->getReminders();
        foreach($reminders as $reminder){
            $election->removeReminder($reminder);
            $em->persist($election);
            $em->flush();
            $em->remove($reminder);
            $em->flush();
        }

        $em->remove($election);
        $em->flush();

        return $this->redirect($this->generateUrl('elections_show'));
	}

	/**
	 * Form to edit an election.
     * @param Request $request the edit request
     * @param int $id the id of the election
     * @return Response the form to edit the election, or the election list.
	 */
	public function editAction(Request $request, $id) {
		// Find the election with an id of $id in the Database		
        $em = $this->getDoctrine()->getManager();
		$repo = $em->getRepository('EasyVoteBundle:Election');
		$election = $repo->findOneById($id);
		
		// Initialize the form with the current election values
        $form = $this->createFormBuilder()
			->add('title', 'text', array(
					'data' => $election->getTitle()					
				)
			)
			->add('summary', 'text', array(
					'data' => $election->getSummary()
				)
			)
			->add('text', 'textarea', array(
					'data' => $election->getText()
				)
			)
			->add('optionSubmissionStartDate', 'date', array(
					'data' => $election->getOptionSubmissionStartDate()
				)
			)
			->add('optionSubmissionEndDate', 'date', array(
					'data' => $election->getOptionSubmissionEndDate()
				)
			)
			->add('votingStartDate', 'date', array(
					'data' => $election->getVotingStartDate()
				)
			)
			->add('votingEndDate', 'date', array(
					'data' => $election->getVotingEndDate()
				)
			)
			->add('addClassifications', 'entity', array(
					'class' => 'EasyVoteBundle:Classification', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'multiple' => false,
					'required' => false
				)
			)
			->add('removeClassifications', 'entity', array(
					'class' => 'EasyVoteBundle:Classification', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'choices' => $election->getClassifications(),
					'multiple' => true,
					'required' => false
				)
			)
			->add('addDegrees', 'entity', array(
					'class' => 'EasyVoteBundle:Degree', 
					'property' => 'displayName' , 
					'empty_value' => 'Choose an option', 
					'multiple' => false,
					'required' => false
				)
			)
			->add('removeDegrees', 'entity', array(
					'class' => 'EasyVoteBundle:Degree', 
					'property' => 'displayName' , 
					'empty_value' => 'Choose an option', 
					'choices' => $election->getDegrees(),
					'multiple' => true,
					'required' => false
				)
			)
			->add('removeReminderDateTime', 'entity', array(
					'class'=>'EasyVoteBundle:ElectionReminder',
					'property'=>'reminderTimeAsString',
					'choices'=>$election->getReminders(),
					'required'=>false,
					'multiple'=>true
				)
			)
			/*
		    ->add('addDepartments', 'entity', array(
					'class' => 'EasyVoteBundle:Department', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'multiple' => true
				)
			)
		    ->add('removeDepartments', 'entity', array(
					'class' => 'EasyVoteBundle:Department', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'choices' => $election->getDepartments(),
					'multiple' => true
				)
			)
			
		    ->add('addJobs', 'entity', array(
					'class' => 'EasyVoteBundle:Job', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'multiple' => true
				)
			)
		    ->add('removeJobs', 'entity', array(
					'class' => 'EasyVoteBundle:Job', 
					'property' => 'name', 
					'empty_value' => 'Choose an option', 
					'choices' => $election->getJobs(),
					'multiple' => true
				)
			)	    
			**/
		    ->add('imageURL', 'url', array(
					'data' => $election->getImageURL()
				)
			)
		    ->add('altText', 'text', array(
					'data' => $election->getAltText()
				)
			)
		    ->add('writeInsAllowed', 'checkbox', array(
					'label' => 'Check if write in candidates are allowed.', 
					'data' => $election->getWriteInsAllowed(),
					'required' => false
				)
			)
		    ->add('isCandidateElection', 'checkbox', array(
					'label' => 'Check if this is a candidate election.', 
					'data' => $election->getIsCandidateElection(),
					'required' => false
				)
			)
			
            ->add('save', 'submit', array('label' => 'Save'))
            ->getForm();

		// Handle Form Submission
        if ($request->getMethod() == 'POST') {
            $form->handleRequest($request);
            if ($form->isValid()) {
				$data = $form->getData();

				if ($data['title'] != null) {					
					if (!empty($data['title'])) {
						$election->setTitle($data['title']);
					}
				}				
				if ($data['summary'] != null) {					
					if (!empty($data['summary'])) {
						$election->setSummary($data['summary']);
					}
				}				
				if ($data['text'] != null) {					
					if (!empty($data['text'])) {
						$election->setText($data['text']);
					}
				}
				if ($data['altText'] != null) {
					if (!empty($data['altText'])) {
						$election->setAltText($data['altText']);
					}
				}
				if ($data['optionSubmissionStartDate'] != null) {					
					if (!empty($data['optionSubmissionStartDate'])) {
						$election->setOptionSubmissionStartDate($data['optionSubmissionStartDate']);
					}
				}
				if ($data['optionSubmissionEndDate'] != null) {					
					if (!empty($data['optionSubmissionEndDate'])) {
						$election->setOptionSubmissionEndDate($data['optionSubmissionEndDate']);
					}
				}
					
				if ($data['votingStartDate'] != null) {					
					if (!empty($data['votingStartDate'])) {
						$election->setVotingStartDate($data['votingStartDate']);
					}
				}
				if ($data['votingEndDate'] != null) {					
					if (!empty($data['votingEndDate'])) {
						$election->setVotingEndDate($data['votingEndDate']);
					}
				}
				if ($data['imageURL'] != null) {					
					if (!empty($data['imageURL'])) {
						$election->setImageURL($data['imageURL']);
					}
				}
				if ($data['writeInsAllowed'] != null) {					
					if (!empty($data['writeInsAllowed'])) {
						$election->setWriteInsAllowed($data['writeInsAllowed']);
					}
				}
				if ($data['isCandidateElection'] != null) {					
					if (!empty($data['isCandidateElection'])) {
						$election->setIsCandidateElection($data['isCandidateElection']);
					}
				}
				
				if ($data['addClassifications'] != null) {					
					if (!empty($data['addClassifications'])) {
						$election->addClassification($data['addClassifications']);
						$em->persist($election);
						$em->flush();					
					}
				}
				if ($data['removeClassifications'] != null) {					
					if (!empty($data['removeClassifications'])) {
						foreach ($data['removeClassifications'] as $item) {
							$election->removeClassification($item);
							$em->persist($election);
							$em->flush();
						}							
					}
				}		
				if ($data['addDegrees'] != null) {					
					if (!empty($data['addDegrees'])) {
						$election->addDegree($data['addDegrees']);
						$em->persist($election);
						$em->flush();				
					}
				}
				if ($data['removeDegrees'] != null) {					
					if (!empty($data['removeDegrees'])) {
						foreach ($data['removeDegrees'] as $item) {
							$election->removeDegree($item);
							$em->persist($election);
							$em->flush();
						}							
					}
				}
				if($data['removeReminderDateTime'] != null){
					foreach($data['removeReminderDateTime'] as $reminderToRemove){
						$election->removeReminder($reminderToRemove);
						$em->persist($election);
						$em->remove($reminderToRemove);
					}
					$em->flush();
				}
				/*
				if ($data['addDepartments'] != null) {					
					if (!empty($data['addDepartments'])) {
						$election->setDepartments($data['addDepartments']);						
					}
				}
				
				if ($data['removeDepartments'] != null) {					
					if (!empty($data['removeDepartments'])) {
						foreach ($data['removeDepartments'] as $item) {
							$election->removeDepartment($item);
							$em->persist($election);
							$em->flush();
						}							
					}
				}	
				*/
                $em->persist($election);
                $em->flush();

                //redirect
                return $this->redirect($this->generateUrl('elections_show'));
			}
		}
		
		// Return the form to the view
		return $this->render('EasyVoteBundle:Elections:administer.html.twig', array('form_view' => $form->createView()));
	}

    /**
     * @Template
     * This form returns to the election profile page that shows the candidates.
     * @param int $id the id of the election
     * @return array The data that is returned to the user is:
     * election= The election entity
     * optionSubmissionStartDateStatus= True if current date is valid
     * optionSubmissionEndDateStatus= True if current date is valid
     * votingStartDateStatus= True if current date is valid
     * votingEndDateStatus= True if current date is valid
     * writeInsAllowedStatus= True if write ins are allowed
     * isAdmin= True if user is an admin
     * chart= The chart that an admin view should show
     */
    public function candidatesAction($id)
    {
		// Find the election with an id of $id in the Database
		$em = $this->getDoctrine()->getManager();
		$repo = $em->getRepository('EasyVoteBundle:Election');
		$election = $repo->findOneById($id);	
		
		// Get the current user
		$user = $this->getUser();	
		
		// The array the is returned to the view
        $retArr = array();
    
		// Return the election
        $retArr['election'] = $repo->find($id); 
		
		// Check all the dates
        if ($retArr['election'] != null) 
        {
			if (!empty($retArr['election'])) 
			{
				// Figure out whether the dates are true or false
				$now = new DateTime();
				$retArr['optionSubmissionStartDateStatus'] = ($now < $election->getOptionSubmissionStartDate()) ? true : false;
				$retArr['optionSubmissionEndDateStatus'] =  ($now < $election->getOptionSubmissionEndDate()) ? true : false;
				$retArr['votingStartDateStatus'] = ($now < $election->getVotingStartDate()) ? true : false;
				$retArr['votingEndDateStatus'] = ($now < $election->getVotingEndDate()) ? true : false;
				
				// Figure out whether write in's are allowed on this election
				$retArr['writeInsAllowedStatus'] = $election->getWriteInsAllowed();	
			}
		}

		// Checks to see if a user is an admin		
		$user = $this->getUser();
		$retArr['isAdmin'] = $user->isAdmin();	
			
		// Return the statistic charts
        $startdate = new DateTime();
        $enddate = new DateTime();
        $startdate->modify('-24 hours');
        $increment = DateInterval::createFromDateString('1 hour');
        $retArr['chartVotesByTime'] = $this->votesByTimeAction($startdate, $enddate, $election, $increment, 'H:i', 'timechart', 'Votes by Time', 'Number of Votes');
        $retArr['chartVotesByCandidate'] = $this->votesByCandidateAction($election, 'piechart', 'Votes by Candidate', 'Number of Votes');
        $retArr['chartVotesBySingleValuedAttributeInUserAction'] = $this->votesBySingleValuedAttributeInUserAction($election, 'classification', 'name', 'classchart', 'Votes by Classification', 'Number of Votes');
        $retArr['chartVotesByMultiValuedAttributeInUserAction'] = $this->votesByMultiValuedAttributeInUserAction($election, 'scopedAffiliations', 'value', 'scopedchart', 'Votes By Scoped Affiliation', 'Number of Votes');

		// Check for user classification
		$validClassification = false;
		$userClassification = $user->getClassification();
		foreach ($election->getClassifications() as $electionClassification) {
			if ($electionClassification == $userClassification) {
				$validClassification = true;
				break;
			}
		}
		
		// Check for user degree
		$validDegree = false;
		foreach ($election->getDegrees() as $electionDegree) {
			foreach ($user->getDegrees() as $userDegree) {
				if ($electionDegree == $userDegree) {
					$validDegree = true;
					break;
				}
			}
		}

		// Return true if the user can vote
		if ($validClassification && $validDegree) {
			$retArr['canVote'] = true;
		}
		else {
			$retArr['canVote'] = false;	
		}

        $userRepo = $em->getRepository('EasyVoteBundle:User');
        $retArr['user'] = $userRepo->findOneById('user');

        if ($retArr['user'] == null) {
            $retArr['user'] = $this->getUser();
        }

        // Return true if the user needs to update their profile
        $retArr['isProfileComplete'] = UserRepository::isProfileComplete($this->getUser()->getProfile());
				
        return $retArr;
    }
    
    /**
     * Creates a chart for votes by time.
     * @param DateTime $startTime The start time
     * @param DateTime $endTime The end time
     * @param Election $election The election
     * @param DateInterval $dateInterval the date interval
     * @param string $formatstr The format string for formatting the datetime objects in the legend
     * @param string $renderTarget the target of $ob->chart->renderTo();
     * @param string $title the title of the chart
     * @param string $seriesName the name of the series
     * @return Highchart
     */
    private function votesByTimeAction(\DateTime $startTime, \DateTime $endTime, Election $election, \DateInterval $dateInterval, $formatstr, $renderTarget, $title, $seriesName)
    {
        //get categories of date times.
        $dateTimes = array();
        for ($currTime = clone $startTime; $currTime <= $endTime; $currTime = $currTime->add($dateInterval)) {
            $dateTimes[] = clone $currTime;
        }

        //get votes
        $votes = $election->getVotes();
        $counts = array();
        for ($i = 0; $i < count($dateTimes); $i++) {
            if (!isset($counts[$i])) {
                $counts[$i] = 0;
            }
        }
        foreach ($votes as $vote) {
            //bounds check
            if ($vote->getUpdatedAt() < $startTime || $vote->getUpdatedAt() > $endTime) {
                continue;
            }

            //find index that exceeds or equals the update time of the vote.
            $foundIndex = -1;
            for ($i = 0; $i < count($dateTimes); $i++) {
                if ($dateTimes[$i] >= $vote->getUpdatedAt()) {
                    $foundIndex = $i;
                    break;
                }
            }

            //now add to array
            if (isset($counts[$foundIndex])) {
                $counts[$foundIndex] = $counts[$foundIndex] + $vote->getWeight();
            } else {
                $counts[$foundIndex] = $vote->getWeight();
            }
        }

        //now format the legend (DateTime instances)
        $legendDateTimes = array();
        foreach ($dateTimes as $dateTime) {
            $legendDateTimes[] = $dateTime->format($formatstr);
        }

        $ob = new Highchart();
        $ob->chart->renderTo($renderTarget);
        $ob->chart->type('column');
        $ob->title->text($title);
        $ob->xAxis->categories($legendDateTimes);
        $ob->series(array(
            array(
                'name' => $seriesName,
                'data' => $counts
            )
        ));
        return $ob;
    }
   
    /**
     * @Template
     * Adds a new candidate to an election
     * @param Request $request the request
     * @param int $id the id of the election
     * @return Response a form to add a new candidate, or the list of candidates in the election.
     */
    public function newCandidateAction(Request $request, $id)
    {
        $em = $this->getDoctrine()->getManager();
		$repo = $em->getRepository('EasyVoteBundle:Election');
		$election = $repo->findOneById($id);
		$userRepo = $em->getRepository('EasyVoteBundle:User');	
		$user = $this->getUser();
		
		if ($election->getIsCandidateElection()) {
			// add a candidate
	        $form = $this->createFormBuilder()
            ->add('euid', 'text')
            ->add('save', 'submit', array('label' => 'Create Candidate'))
            ->getForm();
            
             //this block has "what to do if form is being submitted"
			if ($request->getMethod() == 'POST') {
				$form->handleRequest($request);
				if ($form->isValid()) {				
					// Saves the profile to the database
					$data = $form->getData();
					
					if ($data['euid'] != null) {					
						if (!empty($data['euid'])) {
							$euid = $data['euid'];
							$userToAdd = $userRepo->findOneByEuid($euid);
							if ($userToAdd) {
								$userProfile = $userToAdd->getProfile();
                                if($userProfile != null) {
                                    // if the user is an admin also add the profile
                                    if ($user->isAdmin() == true) {
                                        $election->addProfile($userProfile);
                                    } // else just add as a write in candidate
                                    else {
                                        $election->addWriteInProfile($userProfile);
                                    }
                                }
							}
							else {
								// User EUID not found
							}
						}
					}			
					// Saves the election to the database
					$em->persist($election);
					$em->flush();

					//redirect
					return $this->redirect($this->generateUrl('elections_candidates', array('id' => $id)));
				}
			}		
		}
		else {
	        $form = $this->createFormBuilder()
				->add('title', 'text')
				->add('summary', 'text')
				->add('ftext', 'textarea')
				->add('picture', 'url', array(
						'data' => "http://placehold.it/200x200"
					)
				)
				->add('save', 'submit', array('label' => 'Create Candidate'))
				->getForm();
				
			if ($request->getMethod() == 'POST') {
				$form->handleRequest($request);
				if ($form->isValid()) {	
					$data = $form->getData();

					$newProfile = new Profile();
					if ($data['title'] != null) {					
						if (!empty($data['title'])) {
							$newProfile->setTitle($data['title']);
						}
					}					
					if ($data['summary'] != null) {					
						if (!empty($data['summary'])) {
							$newProfile->setSummary($data['summary']);
						}
					}
					if ($data['ftext'] != null) {					
						if (!empty($data['ftext'])) {
							$newProfile->setText($data['ftext']);
						}
					}	 
					if ($data['picture'] != null) {					
						if (!empty($data['picture'])) {
							$newProfile->setPicture($data['picture']);
						}
					}
					$em->persist($newProfile);
					$em->flush();
					
					if ($this->getUser()->isAdmin()) {
						$election->addProfile($newProfile);
					}
					else {
						$election->addWriteInProfile($newProfile);
					}
					
					// Saves the election to the database
					$em->persist($election);
					$em->flush();

					//redirect
					return $this->redirect($this->generateUrl('elections_candidates', array('id' => $id)));
				}
			}
		}
		
        //this block has "show the form"
        return $this->render('EasyVoteBundle:Elections:newCandidate.html.twig', array('form_view' => $form->createView()));
    }

    /**
     * Approves a candidate in an election.
     * @param int $election The election ID
     * @param int $candidate The profile ID
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function approveCandidateAction($election, $candidate)
    {
        //get the repos
        $em = $this->getDoctrine()->getManager();
        $electionRepo = $em->getRepository('EasyVoteBundle:Election');
        $profileRepo = $em->getRepository('EasyVoteBundle:Profile');

        //get the objects from the IDs
        $electionObj = $electionRepo->findOneById($election);
        $profileObj = $profileRepo->findOneById($candidate);

        if(empty($electionObj) || empty($profileObj) ){
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //check that the adding period is OK
        $now = new DateTime();
        if ($now < $electionObj->getOptionSubmissionStartDate() || $now > $electionObj->getOptionSubmissionEndDate()) {
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //check that the user is an admin
        $user = $this->getUser();
        if(!$user->isAdmin()){
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //check that the write-in exists
        if($electionObj->removeWriteInProfile($profileObj)){
            $electionObj->addProfile($profileObj);
            $em->persist($electionObj);
            $em->flush();
        }

        // This should redirect back to the associated elections page
        return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
    }

    /**
     * Denies/Deletes a profile from an election.
     * @param int $election The election ID
     * @param int $candidate The candidate ID
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function denyCandidateAction($election, $candidate)
    {
        //get the repos
        $em = $this->getDoctrine()->getManager();
        $electionRepo = $em->getRepository('EasyVoteBundle:Election');
        $profileRepo = $em->getRepository('EasyVoteBundle:Profile');

        //get the objects from the IDs
        $electionObj = $electionRepo->findOneById($election);
        $profileObj = $profileRepo->findOneById($candidate);

        if(empty($electionObj) || empty($profileObj) ){
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //check that the period is OK
        $now = new DateTime();
        if ($now < $electionObj->getOptionSubmissionStartDate() || $now > $electionObj->getOptionSubmissionEndDate()) {
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //check that the user is an admin  
        $user = $this->getUser();
        if(!$user->isAdmin()){
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //now remove from everything.
        $electionObj->removeProfile($profileObj);
        $electionObj->removeWriteInProfile($profileObj);
        
       // Remove all the votes for this candidate
		foreach ($electionObj->getVotes() as $vote) {
			if ($vote->getProfile() == $profileObj) {
				$electionObj->removeVote($vote);
				$em->remove($vote);
			}
		}
        
        $em->persist($electionObj);
        $em->flush();

        // This should redirect back to the associated elections page
        return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
    }

    /**
     * Casts a vote for a user in an election.
     * @param $election
     * @param $candidate
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function voteAction($election, $candidate)
    {
        //get the repos
        $em = $this->getDoctrine()->getManager();
        $electionRepo = $em->getRepository('EasyVoteBundle:Election');
        $profileRepo = $em->getRepository('EasyVoteBundle:Profile');
        $voteRepo = $em->getRepository('EasyVoteBundle:Vote');

        //get the objects from the IDs
        $electionObj = $electionRepo->findOneById($election);
        $profileObj = $profileRepo->findOneById($candidate);

        //get the current user
        $user = $this->getUser();

        //check that the period is OK
        $now = new DateTime();
        if ($now < $electionObj->getVotingStartDate() || $now > $electionObj->getVotingEndDate()) {
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //check that option is in the election.
        if(!$electionObj->getProfiles()->contains($profileObj)){
            return $this->redirect($this->generateUrl('elections_candidates', array('id' => $election)));
        }

        //The DB supports weighted votes but for now use single vote with default weight of 1.0
       //check for vote
        foreach($electionObj->getVotes($electionObj) as $vote){
            //found vote.
            if($vote->getUser() == $user){
                $vote->setProfile($profileObj);
                $em->persist($vote);
                $em->flush();
                return $this->redirect($this->generateUrl('vote_view',array('id' => $vote->getID())));
            }
        }

        //add new vote.
        $mVote = new Vote();
        $mVote->setProfile($profileObj);
        $mVote->setUser($user);
        $mVote->setElection($electionObj);
        $mVote->setWeight(1.0);
        $em->persist($mVote);
        $electionObj->addVote($mVote);
        $em->persist($electionObj);
        $em->flush();

        // After voting, render vote success view
        return $this->redirect($this->generateUrl('vote_view',array('id' => $mVote->getID())));
    }
    
    /**
     * Creates a chart of votes by candidate.
     * @param Election $election
     * @param string $renderTo the target to render the Highchart to.
     * @param string $title the title of the chart
     * @param string $seriesLabel the label for the series
     * @return Highchart
     */
    private function votesByCandidateAction(Election $election, $renderTo, $title, $seriesLabel)
    {
        // Chart that prints out the number of votes for each candidate
        $ob = new Highchart();
        $ob->chart->renderTo($renderTo);
        $ob->title->text($title);
        $ob->plotOptions->pie(array(
            'allowPointSelect' => true,
            'cursor' => 'pointer',
            'dataLabels' => array('enabled' => false),
            'showInLegend' => true
        ));

        //get votes
        $votes = $election->getVotes();
        $counts = array();
        foreach ($votes as $vote) {
            if (isset ($counts[$vote->getProfile()->getID()])) {
                $counts[$vote->getProfile()->getID()] += $vote->getWeight();
            } else {
                $counts[$vote->getProfile()->getID()] = $vote->getWeight();
            }
        }

        //get repo
        $em = $this->getDoctrine()->getManager();
        $profileRepo = $em->getRepository('EasyVoteBundle:Profile');

        //make the new array
        $votesData = array();
        foreach ($counts as $id => $count) {
            $profile = $profileRepo->findOneById($id);
            $user = $profile->getUser();

            if ($user) {
                $displayName = $user->getDisplayName();
                $euid = $user->getEuid();
                if (!empty($displayName)) {
                    $votesData[] = array($displayName, $count);
                } else if (!empty($euid)) {
                    $votesData[] = array($euid, $count);
                } else {
                    $votesData[] = array($user->getId(), $count);
                }
            } else {
                $title = $profile->getTitle();
                if (!empty($title)) {
                    $votesData[] = array($title, $count);
                } else {
                    $votesData[] = array($profile->getId(), $count);
                }
            }
        }

        $ob->series(array(array('type' => 'pie', 'name' => $seriesLabel, 'data' => $votesData)));

        return $ob;
    }

    /**
     * @param Election $election The election
     * @param string $renderTo The id of the div
     * @param string $title The title of the chart
     * @param string $seriesLabel The label for the series
     * @param DateTime $startTime The start time
     * @param DateTime $endTime The end time
     * @return Highchart
     */
    public function votesByTimeAndCandidateAction(Election $election, $renderTo, $title, $seriesLabel, DateTime $startTime, DateTime $endTime){
        // Chart that prints out the number of votes for each candidate
        $ob = new Highchart();
        $ob->chart->renderTo($renderTo);
        $ob->title->text($title);
        $ob->plotOptions->pie(array(
            'allowPointSelect' => true,
            'cursor' => 'pointer',
            'dataLabels' => array('enabled' => false),
            'showInLegend' => true
        ));

        //get votes
        $votes = $election->getVotes();
        $counts = array();
        foreach ($votes as $vote) {
            //bounds check
            if ($vote->getUpdatedAt() < $startTime || $vote->getUpdatedAt() > $endTime) {
                continue;
            }
            else if (isset ($counts[$vote->getProfile()->getID()])) {
                $counts[$vote->getProfile()->getID()] += $vote->getWeight();
            } else {
                $counts[$vote->getProfile()->getID()] = $vote->getWeight();
            }
        }

        //get repo
        $em = $this->getDoctrine()->getManager();
        $profileRepo = $em->getRepository('EasyVoteBundle:Profile');

        //make the new array
        $votesData = array();
        foreach ($counts as $id => $count) {
            $profile = $profileRepo->findOneById($id);
            $user = $profile->getUser();

            if ($user) {
                $displayName = $user->getDisplayName();
                $euid = $user->getEuid();
                if (!empty($displayName)) {
                    $votesData[] = array($displayName, $count);
                } else if (!empty($euid)) {
                    $votesData[] = array($euid, $count);
                } else {
                    $votesData[] = array($user->getId(), $count);
                }
            } else {
                $title = $profile->getTitle();
                if (!empty($title)) {
                    $votesData[] = array($title, $count);
                } else {
                    $votesData[] = array($profile->getId(), $count);
                }
            }
        }

        $ob->series(array(array('type' => 'pie', 'name' => $seriesLabel, 'data' => $votesData)));

        return $ob;
    }

    /**
     * Creates a pie chart of votes for a single-valued attribute in the vote's User (the person who cast the vote).
     * @param Election $election
     * @param string $attribute The attribute to access
     * @param string $displayKey If $attribute is another entity, use this field for labeling.
     * @param string $renderTarget the target to render the plot to.
     * @param string $title the title of the chart
     * @param string $seriesName the name of the series
     * @return Highchart
     */
    private function votesBySingleValuedAttributeInUserAction(Election $election, $attribute, $displayKey, $renderTarget, $title, $seriesName)
    {
        $accessor = PropertyAccess::createPropertyAccessor();

        $ob = new Highchart();
        $ob->chart->renderTo($renderTarget);
        $ob->title->text($title);
        $ob->plotOptions->pie(array(
            'allowPointSelect' => true,
            'cursor' => 'pointer',
            'dataLabels' => array('enabled' => false),
            'showInLegend' => true
        ));

        //get votes
        $votes = $election->getVotes();
        $counts = array();
        foreach ($votes as $vote) {
            $user = $vote->getUser();
            $value = $accessor->getValue($user, $attribute);
            $indexValue = $value;

            //ignore empty values.
            if(empty($value)){
                continue;
            }

            //convert object to something that can be used for indexing.
            else if(is_object($value) || is_array($value)){
                if($displayKey){
                    $indexValue = $accessor->getValue($value, $displayKey);
                    if(empty($indexValue)){
                        continue;
                    }
                }
                else{
                    continue;
                }
            }

            if (isset($counts[$indexValue])) {
                $counts[$indexValue] += $vote->getWeight();
            } else {
                $counts[$indexValue] = $vote->getWeight();
            }
        }

        $votesData = array();
        foreach ($counts as $value => $count) {
            $votesData[] = array($value, $count);
        }

        $ob->series(array(array('type' => 'pie', 'name' => $seriesName, 'data' => $votesData)));

        return $ob;
    }



    /**
     * Creates a chart of votes for a multi-valued attribute in the vote's User (the person who cast the vote).
     * @param Election $election
     * @param string $attribute The attribute to access
     * @param string $displayKey If $attribute is another entity, use this field for labeling.
     * @param string $renderTarget the target to render the plot to.
     * @param string $title the title of the chart
     * @param string $seriesName the name of the series
     * @return Highchart
     */
    private function votesByMultiValuedAttributeInUserAction(Election $election, $attribute, $displayKey, $renderTarget, $title, $seriesName)
    {
        $accessor = PropertyAccess::createPropertyAccessor();

        //get votes
        $votes = $election->getVotes();
        $counts = array();
        foreach ($votes as $vote) {
            $user = $vote->getUser();
            $values = $accessor->getValue($user, $attribute);
            foreach ($values as $value) {
                $indexValue = $value;

                //ignore empty values.
                if(empty($value)){
                    continue;
                }

                //convert object to something that can be used for indexing.
                else if(is_object($value) || is_array($value)){
                    if($displayKey){
                        $indexValue = $accessor->getValue($value, $displayKey);
                        if(empty($indexValue)){
                            continue;
                        }
                    }
                    else{
                        continue;
                    }
                }

                if (isset($counts[$indexValue])) {
                    $counts[$indexValue] += $vote->getWeight();
                } else {
                    $counts[$indexValue] = $vote->getWeight();
                }
            }
        }

        //organize into legend and data
        $votesData = array();
        $legendData = array();
        foreach ($counts as $value => $count) {
            $legendData[] = $value;
            $votesData[] = $count;
        }

        $ob = new Highchart();
        $ob->chart->renderTo($renderTarget);
        $ob->chart->type('column');
        $ob->title->text($title);
        $ob->xAxis->categories($legendData);
        $ob->series(array(
            array(
                'name' => $seriesName,
                'data' => $votesData
            )
        ));
        return $ob;
    }

    /**
     * An action to lookup a vote by id and display a receipt.
     * @param int $id the id of the vote
     * @return Object
     */
    public function voteSuccessAction($id){
        $em = $this->getDoctrine()->getManager();
        $vote = $em->getRepository('EasyVoteBundle:Vote')->find($id);

        //condition 1: vote does not exist
        if(!$vote){
            return $this->redirect($this->generateUrl('elections_show'));
        }

        //condition 2: you can only see your own receipt unless you are an admin
        else if($vote->getUser()->getID() != $this->getUser()->getID() && !$this->getUser()->isAdmin()) {
            return $this->redirect($this->generateUrl('elections_show'));
        }
        else{
            return $this->render('EasyVoteBundle:Elections:voteSuccess.html.twig', array('vote' => $vote));
        }
    }

	/**
	 * Adds a scheduled reminder for an election.
	 * @param Request $request The HTTP request
	 * @param int $id The id of the election
	 * @return \Symfony\Component\HttpFoundation\Response The form to display, or a redirect to the election list.
	 */
	public function reminderScheduleAction(Request $request, $id){
		$em = $this->getDoctrine()->getManager();
		$election = $em->getRepository('EasyVoteBundle:Election')->find($id);

		//exit condition 1: election DNE
		if(!$election){
			return $this->redirect($this->generateUrl('elections_show'));
		}

		//exit condition 2: user is not admin
		else if(!$this->getUser()->isAdmin()){
			return $this->redirect($this->generateUrl('elections_show'));
		}

		//exit condition 3: voting ended. No point.
		else if($election->getVotingEndDate() <= new DateTime()){
			return $this->redirect($this->generateUrl('elections_show'));
		}

		//otherwise this is ok.
		$form = $this->createFormBuilder()
			->add('addReminderDateTime', 'datetime', array(
					'required'=>false,
					'data'=>new DateTime(),
					'date_widget'=>'single_text',
					'time_widget'=>'single_text',
				)
			)
			->add('save', 'submit', array('label' => 'Save'))
			->getForm();

		if($request->getMethod() == 'POST'){
			$form->handleRequest($request);
			if($form->isValid()) {
				$data = $form->getData();
				if ($data['addReminderDateTime'] != null) {
					$reminder = new ElectionReminder();
					$reminder->setElection($election);
					$reminder->setReminderTime($data['addReminderDateTime']);
					$em->persist($reminder);
					$election->addReminder($reminder);
					$em->persist($election);
					$em->flush();
				}

				return $this->redirect($this->generateUrl('reminder_schedule', array('id'=>$id)));
			}
		}

		return $this->render('EasyVoteBundle:Elections:reminderSchedule.html.twig', array('form' => $form->createView()));
	}
}
